package com.lxm.framework.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.lxm.framework.common.UsualConstant;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * @Author: Lys
 * @Date 2023/2/7
 * @Describe
 **/
@Configuration
public class RedisDbConfig {

    @Bean("lxmRedisConnectionFactory")
    public JedisConnectionFactory lxmRedisConnectionFactory(RedisProperties properties) {
        var standaloneConfig = new RedisStandaloneConfiguration();
        standaloneConfig.setHostName(properties.getHost());
        standaloneConfig.setPassword(RedisPassword.of(properties.getPassword()));
        standaloneConfig.setPort(properties.getPort());
        standaloneConfig.setDatabase(properties.getDatabase());
        JedisClientConfiguration clientConfiguration = getJedisClientConfiguration();
        JedisConnectionFactory connectionFactory = new JedisConnectionFactory(standaloneConfig, clientConfiguration);
        // 这里有个坑，貌似是2.6.2之后的改动，new出来这个玩意儿之后，要手动afterPropertiesSet一下。
        // 改动里面的属性this.initialized = true;还不知道为什么这么设计
        connectionFactory.afterPropertiesSet();
        return connectionFactory;
    }

    @Bean("lxmRedisContainer")
    public RedisMessageListenerContainer lxmRedisContainer(@Qualifier("lxmRedisConnectionFactory") JedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }


    @Bean("lxmRedisTemplate")
    public RedisTemplate<String, Object> lxmRedisTemplate(@Qualifier("lxmRedisConnectionFactory") JedisConnectionFactory lxmRedisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        lxmRedisConnectionFactory.afterPropertiesSet();
        template.setConnectionFactory(lxmRedisConnectionFactory);
        var om = getOm();
        // spring-data-redis 3.X之后的改动，之前是通过valueSerializer.setObjectMapper(om)设置，之后通过构造器
        var valueSerializer = new Jackson2JsonRedisSerializer<>(om, Object.class);
        template.setValueSerializer(valueSerializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    @Bean("lxmRedisLocker")
    public StringRedisTemplate lxmRedisLocker(@Qualifier("lxmRedisConnectionFactory") JedisConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
    }

    private ObjectMapper getOm() {
        var om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        var module = getSimpleModule();
        om.registerModule(module);
        om.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        return om;
    }

    private SimpleModule getSimpleModule() {
        var module = new SimpleModule();
        module.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(UsualConstant.DEFAULT_TIME_FORMAT)));
        module.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(UsualConstant.DEFAULT_DATE_FORMAT)));
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(UsualConstant.DEFAULT_DATETIME_FORMAT)));
        module.addSerializer(Date.class, new DateSerializer(false, new SimpleDateFormat(UsualConstant.DEFAULT_DATETIME_FORMAT)));
        module.addSerializer(Timestamp.class, new DateSerializer(false, new SimpleDateFormat(UsualConstant.DEFAULT_DATETIME_FORMAT)));
        module.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(UsualConstant.DEFAULT_TIME_FORMAT)));
        module.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(UsualConstant.DEFAULT_DATE_FORMAT)));
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(UsualConstant.DEFAULT_DATETIME_FORMAT)));
        module.addDeserializer(Date.class, new DateDeserializers.DateDeserializer(DateDeserializers.DateDeserializer.instance, new SimpleDateFormat(UsualConstant.DEFAULT_DATETIME_FORMAT), UsualConstant.DEFAULT_DATETIME_FORMAT));
        module.addDeserializer(Timestamp.class, new DateDeserializers.TimestampDeserializer(new DateDeserializers.TimestampDeserializer(), new SimpleDateFormat(UsualConstant.DEFAULT_DATETIME_FORMAT), UsualConstant.DEFAULT_DATETIME_FORMAT));
        return module;
    }

    private JedisClientConfiguration getJedisClientConfiguration() {
        var builder = JedisClientConfiguration.builder();
        var timeout = Duration.ofSeconds(10);
        builder.readTimeout(timeout).connectTimeout(timeout);
        builder.usePooling().poolConfig(getJedisPoolConfig());
        return builder.build();
    }

    private JedisPoolConfig getJedisPoolConfig() {
        var jedisPoolConfig = new JedisPoolConfig();
        // 最大连接数
        jedisPoolConfig.setMaxTotal(8);
        // 最大能保持空闲的数量
        jedisPoolConfig.setMaxIdle(8);
        jedisPoolConfig.setMinIdle(0);
        jedisPoolConfig.setMaxWait(Duration.ofMillis(1500));
        return jedisPoolConfig;
    }

}
